﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Markup;

using alib;
using alib.Collections;
using alib.Combinatorics;
using alib.Debugging;
using alib.Dictionary;
using alib.Enumerable;
using alib.Hashing;
using alib.Reflection;

using agree.schema;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// base class which discharges or forwards unwanted modification-related and non-generic variants
	/// of interface members for read-only collections, allowing for less clutter in the derived classes
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[RuntimeNameProperty("Name")]
	public abstract class ial_base<T> : identity_base, IIdentAddList<T>
	{
		public ial_base(IIdentity prv)
			: base(prv)
		{
		}
		public abstract T this[int index] { get; set; }

		public abstract int Count { get; }

		public abstract int IndexOf(T item);

		public abstract IEnumerator<T> GetEnumerator();

		void ICollection.CopyTo(Array array, int index)
		{
			var e = this.GetEnumerator();
			while (e.MoveNext())
				array.SetValue(e.Current, index++);
		}

		void ICollection<T>.CopyTo(T[] array, int arrayIndex)
		{
			var e = this.GetEnumerator();
			while (e.MoveNext())
				array[arrayIndex++] = e.Current;
		}

		public bool Contains(T item) { return IndexOf(item) != -1; }

		Object IList.this[int index] { get { return this[index]; } set { throw not.valid; } }
		T IList<T>.this[int index] { get { return this[index]; } set { this[index] = value; } }

		int IList.Add(Object value) { throw not.valid; }
		int IList.IndexOf(Object value) { return value is T ? IndexOf((T)value) : -1; }
		void IList.Insert(int index, Object value) { throw not.valid; }
		void IList.Remove(Object value) { throw not.valid; }
		void IList.RemoveAt(int index) { throw not.valid; }
		void IList.Clear() { throw not.valid; }
		bool IList.IsReadOnly { get { return false; } }
		bool IList.IsFixedSize { get { return true; } }
		void IList<T>.Insert(int index, T item) { throw not.valid; }
		void IList<T>.RemoveAt(int index) { throw not.valid; }
		bool IList.Contains(Object value) { return value is T && Contains((T)value); }
		void IAddRange<T>.AddRange(IEnumerable<T> rg) { throw not.valid; }
		bool ICollection<T>.Remove(T item) { throw not.valid; }
		void ICollection<T>.Add(T item) { throw not.valid; }
		void ICollection<T>.Clear() { throw not.valid; }
		bool ICollection<T>.IsReadOnly { get { return false; } }
		bool ICollection.IsSynchronized { get { return false; } }
		Object ICollection.SyncRoot { get { return this; } }
		IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Instantiated collection of Ts which can participate in the pipline, either via spooling or whole-
	/// instance passing.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class IdentArray<T> : ial_base<T>, IIdentList<T>
	{
		public IdentArray(IIdentity prv, T[] arr)
			: base(prv)
		{
			this.arr = arr ?? alib.Collections.Collection<T>.None;
		}
		public IdentArray(IIdentity prv, int c)
			: this(prv, c == 0 ? null : new T[c])
		{
		}
		public IdentArray(IIdentity prv, RefList<T> reflist)
			: base(prv)
		{
			this.arr = reflist.GetTrimmed();
		}
		public IdentArray(IIdentity prv, IIdentList<T> to_copy)
			: base(prv)
		{
			IdentArray<T> ia;
			if ((ia = to_copy as IdentArray<T>) != null)
				this.arr = ia.arr;
			else
			{
				Nop.CodeCoverage("IdentArray");
				this.arr = to_copy.ToArray();
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public readonly T[] arr;

		public override T this[int index] { [DebuggerStepThrough] get { return arr[index]; } [DebuggerStepThrough] set { arr[index] = value; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public override int Count { get { return arr.Length; } }

		public override int IndexOf(T item) { return Array.IndexOf<T>(arr, item); }

		public override IEnumerator<T> GetEnumerator() { return arr.Enumerator(); }

		public override String ToString()
		{
			return String.Format("{0} count:{1}", this.GetType()._Name(), arr.Length);
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[RuntimeNameProperty("Name")]
	[ContentProperty("Items")]
	//[DebuggerDisplay("{ToString(),nq}")]
	//[DebuggerTypeProxy(typeof(IdentList<>._iidentity_debug_list))]
	//[DebuggerTypeProxy(typeof(_iidentity_debug))]
	public class IdentList<T> : RefList<T>, IIdentList<T>, IIdentAddList<T>, ρρItemsArr
		where T : class, IIdentity
	{
		public IdentList(IIdentity prv, IEnumerable<T> items, int capacity = -1)
			: base(capacity <= 0 ? 5 : capacity)
		{
			this.trace = prv;
			if (items != null)
			{
				var e = items.GetEnumerator();
				while (e.MoveNext())
					base.Add(e.Current);
			}
		}
		public IdentList(IIdentity prv)
			: this(prv, default(IEnumerable<T>))
		{
		}

		public IReadOnlyList<T> Items
		{
			get { return this; }
			set
			{
				base.Clear();
				base.AddRange(value);
			}
		}

		IIdentity[] ρρItemsArr.ItemsArr { get { return base.GetTrimmed(); } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		IIdentity trace;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IIdentity Trace { get { return this.trace; } }

#if false
		public Object _get_debug(IIdentity item) { return new _iidentity_debug_list(item); }

		[DebuggerDisplay("{item.ToString(),nq}")]
		public sealed class _iidentity_debug_list
		{
			public _iidentity_debug_list(IdentList<T> item)
			{
				this.item = item;
			}
			public _iidentity_debug_list(IIdentity item)
				: this((IdentList<T>)item)
			{
			}
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			readonly IdentList<T> item;

			[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
			public item_property_value[] _props_display
			{
				get { return _identity_extensions.PropertyValues(item); }
			}

			[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
			public Object[] _dbg_items
			{
				get
				{
					IList<T> o = item;
					int c = o.Count;
					var arr = new Object[c];
					for (int i = 0; i < c; i++)
						arr[i] = _iidentity_debug.Get(o[i]);
					return arr;
				}
			}
		}
#endif
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class IdentityList : IdentList<IIdentity>
	{
		public IdentityList(IIdentity prv, IEnumerable<IIdentity> items)
			: base(prv)
		{
		}
		public IdentityList(IIdentity prv)
			: this(prv, default(IEnumerable<IIdentity>))
		{
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[ContentProperty("Items")]
	public class ItemsList<T> : identity_base, IReadOnlyList<T>, IAddRangeList<T>
	{
		public ItemsList(IIdentity prv, IEnumerable<T> items)
			: base(prv)
		{
			List<T> L;
			this.list = L = new List<T>();
			if (items != null)
				L.AddRange(items);
		}
		public ItemsList(IIdentity prv)
			: this(prv, default(IEnumerable<T>))
		{
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IList<T> Items
		{
			get { return this.list; }
			set { Version++; this.list = value; }
		}

		public int Version;
		public long VersionHash
		{
			get
			{
				return ((long)list.GetHashCode() << 32) ^ (this.GetHashCode() ^ (this.Version << 16) ^ this.Count);
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		protected IList<T> list;
		//[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		//protected IList<T> _dbg_items { get { return list; } }

		public void Clear() { Version++; list.Clear(); }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int Count { get { return list.Count; } }
		public T this[int index] { get { return list[index]; } set { Version++; list[index] = value; } }

		public int IndexOf(T item) { return list.IndexOf(item); }
		public bool Contains(T item) { return list.Contains(item); }
		public bool Add(T item) { Version++; list.Add(item); return true; }
		public bool Remove(T item) { Version++; return list.Remove(item); }
		public void Insert(int index, T item) { Version++; list.Insert(index, item); }
		public void AddRange(IEnumerable<T> items)
		{
			Version++;
			((List<T>)list).AddRange(items);
		}
		public void RemoveAt(int index) { Version++; list.RemoveAt(index); }

		public IEnumerator<T> GetEnumerator() { return list.GetEnumerator(); }
		IEnumerator IEnumerable.GetEnumerator() { return list.GetEnumerator(); }
		void ICollection.CopyTo(Array array, int index) { throw not.impl; }
		public void CopyTo(T[] array, int arrayIndex) { list.CopyTo(array, arrayIndex); }

		void ICollection<T>.Add(T item) { Version++; list.Add(item); }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool IsReadOnly { get { return false; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		bool ICollection.IsSynchronized { get { return false; } }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		Object ICollection.SyncRoot { get { return true; } }

		int IList.Add(Object value) { this.Add((T)value); return Count - 1; }
		void IList.Insert(int index, Object value) { this.Insert(index, (T)value); }
		void IList.Remove(Object value) { this.Remove((T)value); }
		bool IList.Contains(Object value) { return this.Contains((T)value); }
		int IList.IndexOf(Object value) { return this.IndexOf((T)value); }
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		bool IList.IsFixedSize { get { return false; } }
		Object IList.this[int index] { get { return this[index]; } set { this[index] = (T)value; } }
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[UsableDuringInitialization(true)]
	[DebuggerDisplay("Count={Count}")]
	public class IgnoreRules : ItemsList<String>
	{
		public IgnoreRules(IIdentity prv)
			: base(prv)
		{
		}

		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		String[] _dbg_disp { get { return this.ToArray(); } }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class IdentIndexedHash<T> : IndexedHash<T>, IIdentList<T>
		where T : IIdentity
	{
		public IdentIndexedHash(IIdentity prv)
		{
			this.prv = prv;
		}
		public IdentIndexedHash(IIdentity prv, int capacity)
			: base(capacity)
		{
			this.prv = prv;
		}
		public IdentIndexedHash(IIdentity prv, IEnumerable<T> items_in)
			: base(items_in)
		{
			this.prv = prv;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		IIdentity prv;
		//[DebuggerDisplay("{identity_base._dbg_trace(this),nq}")]
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IIdentity Trace { [DebuggerStepThrough] get { return prv; } }
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[RuntimeNameProperty("Name")]
	//[DebuggerDisplay("{ToString(),nq}")]
	public abstract class IdentDict<TKey, TValue> : SlotDictionary<TKey, TValue>, IIdentDict<TKey, TValue>,
		ρρName
		where TValue : IIdentity
	{
		public IdentDict(IIdentity prv, int capacity, IEqualityComparer<TKey> cmp)
			: base(capacity, cmp)
		{
			this.trace = prv;
		}

		public IdentDict(IIdentity prv)
			: this(prv, 0, default(IEqualityComparer<TKey>))
		{
		}

		public IdentDict(IIdentity prv, int capacity)
			: this(prv, capacity, default(IEqualityComparer<TKey>))
		{
			this.trace = prv;
		}

		public IdentDict()
			: this(default(IIdentity))
		{
		}

		///////////////////////////////////////////////////////////////////////
		/// 
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		String name;
		public String Name
		{
			get { return this.name; }
			set { this.name = value; }
		}
		/// 
		///////////////////////////////////////////////////////////////////////

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		IIdentity trace;
		//[DebuggerDisplay("{identity_base._dbg_trace(this),nq}")]
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IIdentity Trace { get { return trace; } }

		//[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		//public item_property_value[] _props_display
		//{
		//	get { return _identity_extensions.PropertyValues(this); }
		//}
	};
}
